#include "edgeBase.hpp"
#include "meshTypes.hpp"

namespace hms {

#define REQUIRE_MEMBER_FUNC(FUNC_NAME) \
	static_assert(std::is_member_function_pointer_v< \
		decltype(&EdgeType::FUNC_NAME) \
	>);
#define REQUIRE_RETURNS_2X1_COLUMN_VECTOR(FUNC_NAME) \
	static_assert( decltype(std::declval<EdgeType>().FUNC_NAME()) \
		::ColsAtCompileTime == 1 ); \
	static_assert( decltype(std::declval<EdgeType>().FUNC_NAME()) \
		::RowsAtCompileTime == 2 );

#define CHEAD(RETURN_TYPE) \
template<typename EdgeType> \
RETURN_TYPE EdgeBase<EdgeType>::

/* the constructor checks for existence (but not correctness) of interface
 * implementations during compile time */
CHEAD() EdgeBase(){
	REQUIRE_MEMBER_FUNC(meshImp)
	REQUIRE_MEMBER_FUNC(indexImp)

	REQUIRE_MEMBER_FUNC(lengthImp)
	REQUIRE_MEMBER_FUNC(normalImp)

	REQUIRE_MEMBER_FUNC(verticesImp)
	REQUIRE_MEMBER_FUNC(midpointImp)
	REQUIRE_MEMBER_FUNC(intersectionImp)
	REQUIRE_MEMBER_FUNC(isBoundaryImp)
	REQUIRE_MEMBER_FUNC(cellsImp)
	REQUIRE_MEMBER_FUNC(domainCellImp)
	REQUIRE_MEMBER_FUNC(ghostCellImp)
	REQUIRE_RETURNS_2X1_COLUMN_VECTOR(verticesImp)
	// REQUIRE_RETURNS_2X1_COLUMN_VECTOR(cellsImp)
}

#define FORWARD_FUNC_CALL_NOARG(NAME,RET) \
	CHEAD(auto) NAME() const -> RET { \
		return this->derived().NAME##Imp(); \
	}

/* examples for what this macro expands to */
/*
template<typename EdgeType>
auto EdgeBase<EdgeType>::mesh() const -> const MeshType& {
	return derived().meshImp();
}
*/

FORWARD_FUNC_CALL_NOARG(mesh        ,const MeshType&)
FORWARD_FUNC_CALL_NOARG(index       ,Index)
FORWARD_FUNC_CALL_NOARG(vertices    ,VertexIndices)
FORWARD_FUNC_CALL_NOARG(isBoundary  ,bool)
FORWARD_FUNC_CALL_NOARG(midpoint    ,Vector2s)
FORWARD_FUNC_CALL_NOARG(intersection,Vector2s)

FORWARD_FUNC_CALL_NOARG(normal      ,NormalVector)
FORWARD_FUNC_CALL_NOARG(length      ,scalar)

// CHEAD(auto) normal() const -> NormalVector {
// 	return mesh().edges().normals().col( index() );
// }
// CHEAD(auto) length() const -> scalar {
// 	return mesh().edges().lengths()( index() );
// }


#define FORWARD_FUNC_CALL_INDEX(NAME,RET) \
	CHEAD(auto) NAME(Index i) const -> RET { \
		return this->derived().NAME##Imp(i); \
	}

FORWARD_FUNC_CALL_INDEX(cells     ,CellIndices)
FORWARD_FUNC_CALL_INDEX(domainCell,Index)
FORWARD_FUNC_CALL_INDEX(ghostCell ,Index)


#undef CHEAD
#undef REQUIRE_MEMBER_FUNC
#undef REQUIRE_RETURNS_2X1_COLUMN_VECTOR
#undef FORWARD_FUNC_CALL_NOARG
#undef FORWARD_FUNC_CALL_INDEX

template class EdgeBase<Edge>;
template class EdgeBase<UniEdge>;
template class EdgeBase<RectEdge>;
template class EdgeBase<StructEdge>;

} // end namespace hms